iT邦幫忙

2023 iThome 鐵人賽

DAY 7
0

Table of Contents

  • 建構函式的原型
  • 原生的原型
    • 誰擁有原型?
    • 那原始型別為什麽可以用原生原型的方法?
    • 預設的物件原型
    • 為物件修改原型
    • 原生原型的內建方法
  • new是什麼?
  • new在建構的過程
  • References

昨天提到JS的原型到底是什麽,以及一些內建的Object原型方法,今天來講一下建構函式。

建構函式的原型

In JavaScript, all functions have a property named prototype.

所有類型的函式都有內建prototype的property,而這個的property本身是一個物件,需要透過這個物件擴充需要的屬性或方法。

讓我們實際用程式碼走一次流程:

//新建一個基本函式
function contestant(){}
//使用函式叫做`prototype`的property
console.log(contestant.prototype)

結果如下:

會發現預設的屬性裡面有一個建構子,建構子的內容指向函式本身。

function Contestant(){}
console.log(Contestant.prototype.constructor)//[Function: Contestant]

同理上面的內容,我們使用一般建構物件的函式,也會有一樣的結果:

當我們使用函式當作建構函式的時候,這個屬性(在函式內建叫prototype的屬性)就會變成新建構的物件原型。

function Contestant(id,name){
  this.contestantId = id
  this.contestantName = name
}

const contestant1 = new Contestant(1,'Alice')

console.log(contestant1)//Contestant {contestantId: 1, contestantName: 'Alice'}

試著在prototype裡新增方法,能讓新建構的實例被使用:

function Contestant(id,name){
  this.contestantId = id
  this.contestantName = name
}

Contestant.prototype.sayName = function(){
  return `My name is ${this.contestantName}`
}

const contestant1 = new Contestant(1,'Alice')

console.log(contestant1.sayName())//My name is Alice

當我們讓方法存在建構函式的prototype裡,就能讓一直接下來創造的實例共享這個方法。

原生的原型

上面提到function本身都有一個原型,是為了說明主題能集中在建構函式的原型。但這裡想另外提到,只要物件本身就有自己的原型,而Javascript本身有為了內建物件去設置原型方法。

誰擁有原型?

就目前理解來說,有[[prototype]]的是:物件型別的變數、有建構子的內建物件,但函式的[[prototype]]是叫prototype的屬性。

prototype
原始型別 x
物件型別 O
內建物件建構的實例 O

那原始型別為什麽可以用原生原型的方法?

至於原始型別的變數,MDN上是這樣解釋:

Primitives have no methods but still behave as if they do. When properties are accessed on primitives, JavaScript auto-boxes the value into a wrapper object and accesses the property on that object instead.

這些原始型別沒有任何方法,但當我們想對這些原始型別的值使用方法時,JavaScript會把值包裝在對應的物件裡,並讓我們能夠使用該物件的方法。這個轉換過程的名稱為auto-boxauto-boxing

預設的物件原型

現在我們知道只要是物件型別都有原型,且這些個別的型別都會導向物件型別。比方說Array的[[prototype]]是Array.prototype,而Array.prototype會導向Object.prototype,這應該也是為什麽Array可以使用Object方法的原因,而Object.prototype會是整個物件原型鍊中最上層的prototype。

在先前也有提到會建立預設方法,而不管是哪一種方式建立,同個型別會共享同個原生原型:
首先我創一個物件,讓名為e的原型去新增一個method。

就會發現這樣等同於為Object.prototype新增一個method。

為物件修改原型

之前有聊到我們可以為物件修改它的原型,也能正常使用Object.prototype的方法,但假設物件型別跟繼承過來的原型物件不一樣的話,會導致物件沒辦法使用他的原生方法。

  1. 現在個別使用型別當名稱,分別宣告一個陣列跟一個物件

  2. 確認array[[prototype]]Array.prototype

  3. 把array的prototype改成object,就會發現array的prototype沒有任何預設的Array.prototype的method可以用

上面後續如果不把array原型改回來,而是繼承另一個array的話:

  1. 先宣告另一個陣列叫array1

  2. array繼承array1

  3. 可正常使用array的method

    即使array的原型是array1,還是可以從array1的[[prototype]]繼承方法。

原生原型的內建方法

關於這些原生內建的方法,如果常常逛MDN就會發現有些方法名稱會是:Object.prototype.isPrototypeOf()Array.prototype.forEach()Array.prototype.includes()

我們拿其中一個方法改造一下:

Array.prototype.forEach = function(){
  return 1
}

const array = [1,2,3];
array.forEach(value=>{
  console.log(value)//空的
})
console.log(array.forEach());//1


source:https://memes.tw/wtf?contest=1618

沒錯,原生的方法是可以被蓋掉的,~~也太過自由,~~會導致這些方法不正常。所以使用上是合法的,但最好、最好、最好不要這樣做。

new是什麼?

new是一個運算子,有兩個情況需要使用:

  1. 為建構函式建立實例
  2. 為具有建構子的內建物件建立實例

也就是說,new必須搭配有內建constructor的物件一起使用。

關於第一點,來回顧剛剛的程式碼範例:自訂這個函式的內容,並透過new建立實例。

function Contestant(id,name){
  this.contestantId = id
  this.contestantName = name
}

const contestant1 = new Contestant(1,'Alice')

console.log(contestant1)//Contestant {contestantId: 1, contestantName: 'Alice'}

第二點的話,可以參閱MDN的內建物件,這裡舉例幾個:
new Array()
new Date()
new Boolean()

而且當這些建構函式要建構實例時,有些函式不需要使用new關鍵字也可以被創立:

  const arrayItem1 = Array(1,2,3)
  const arrayItem2 = new Array(1,2,3)

  console.log(arrayItem1);//[ 1, 2, 3 ]
  console.log(arrayItem2);//[ 1, 2, 3 ]

new在建構的過程

當我們在使用對建構函式使用new

  1. 先創建一個乾淨的新實例。
  2. 讓新物件指向的原型有兩個,一個指向建構函式的prototype屬性,另一個則是一般物件的[[prototype]]。
  3. 執行建構函式中的參數,並綁定新實例的this。
  4. 回傳新實例。

References

JavaScript User-defined Object Type
該來理解 JavaScript 的原型鍊了
重新認識 JavaScript: Day 25 原型與繼承
JS 原力覺醒 Day22 - 原型共享與原型繼承
Types of JavaScript Objects: Built-in vs User-defined


  • 忍者:JavaScript開發技巧探秘第二版(第七章)
    JavaScript大全(第六版)

  • MDN

  1. Standard built-in objects
  2. new operator
  3. Function: prototype
  • Javascript Info
  1. F.prototype
  2. Native prototypes

上一篇
〈Day6〉原型
下一篇
〈Day8〉屬性描述器(上)
系列文
廚藝不精也可以,給自己做一份Javascript小火鍋30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言